//
// OLLocrAPIRequest.m
//
// Copyright (c) 2009 Houdah Software s.à r.l. (http://www.houdah.com)
// Copyright (c) 2009 Lukhnos D. Liu (http://lukhnos.org)
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//

#import "OLLocrAPIRequest.h"

#import "OLLocrAPIContext.h"
#import "OLUtilities.h"


NSString *const OLLocrAPIReturnedErrorDomain		= @"com.locr";
NSString *const OLLocrAPIRequestErrorDomain			= @"com.houdah.ObjectiveLocr";

NSString *const OLLocrUploadTempFilenamePrefix		= @"com.houdah.ObjectiveLocr.upload";


@interface OLLocrAPIRequest (PrivateMethods) <OLLocrAPISignatureConsumer>

- (NSArray *)signedArgumentComponentsFromArguments:(NSDictionary *)inArguments
									  useURIEscape:(BOOL)inUseEscape
									authentication:(BOOL)inAuthentication
											 error:(NSError**)error;
- (NSString *)signedQueryFromArguments:(NSDictionary *)inArguments
						authentication:(BOOL)inAuthentication
								 error:(NSError**)error;

- (void)cleanUpTempFile;

@end            


@implementation OLLocrAPIRequest

- (void)dealloc
{
	[self cancel];
	
    [context release], context = nil;
    [httpRequest release], httpRequest = nil;
    [sessionInfo release], sessionInfo = nil;
    
	[signature release], signature = nil;
    [invocation release], invocation = nil;

    [self cleanUpTempFile];
    
    [super dealloc];
}

- (id)initWithAPIContext:(OLLocrAPIContext *)inContext
{
    if (self = [super init]) {
        context = [inContext retain];
        
        httpRequest = [[LFHTTPRequest alloc] init];
        [httpRequest setDelegate:self];
    }
    
    return self;
}

- (OLLocrAPIContext *)context
{
	return context;
}

- (OLLocrAPIRequestDelegateType)delegate
{
    return delegate;
}

- (void)setDelegate:(OLLocrAPIRequestDelegateType)inDelegate
{
    delegate = inDelegate;
}

- (id)sessionInfo
{
    return [[sessionInfo retain] autorelease];
}

- (void)setSessionInfo:(id)inInfo
{
    id tmp = sessionInfo;
    sessionInfo = [inInfo retain];
    [tmp release];
}

- (NSTimeInterval)requestTimeoutInterval
{
    return [httpRequest timeoutInterval];
}

- (void)setRequestTimeoutInterval:(NSTimeInterval)inTimeInterval
{
    [httpRequest setTimeoutInterval:inTimeInterval];
}

- (BOOL)isRunning
{
    return (invocation != nil) || [httpRequest isRunning];
}

- (void)cancel
{
	[invocation release], invocation = nil;
    [httpRequest cancelWithoutDelegateMessage];
	
    [self cleanUpTempFile];
}

- (void)callAPIMethodWithGET:(NSString *)inMethodName
				   arguments:(NSDictionary *)inArguments
			  authentication:(BOOL)inAuthentication
{
    if ([self isRunning]) {
		if ([delegate respondsToSelector:@selector(locrAPIRequest:didFailWithError:)]) {
			NSError *error = [NSError errorWithDomain:OLLocrAPIRequestErrorDomain
												 code:OLLocrAPIRequestConnectionError
											 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Concurrent requests not supported", NSLocalizedFailureReasonErrorKey, nil]];
			
			[delegate locrAPIRequest:self didFailWithError:error];        
		}
		
		return;
    }
    
	if (inAuthentication  && (signature == nil)) {
		SEL mySelector = @selector(callAPIMethodWithGET:arguments:authentication:);
		NSMethodSignature *mySignature = [[self class] instanceMethodSignatureForSelector:mySelector];
		NSInvocation *myInvocation = [NSInvocation invocationWithMethodSignature:mySignature];
		NSNumber *myAuthentication = [NSNumber numberWithBool:inAuthentication];
		
		[myInvocation retainArguments];
		[myInvocation setTarget:self];
		[myInvocation setSelector:mySelector];
		[myInvocation setArgument:&inMethodName atIndex:2];
		[myInvocation setArgument:&inArguments atIndex:3];
		[myInvocation setArgument:&myAuthentication atIndex:4];
		
		invocation = [myInvocation retain];
		
		[context requestSignature:self];
		
		return;
	}
	
    
    // combine the parameters 
	NSMutableDictionary *newArgs = inArguments ? [NSMutableDictionary dictionaryWithDictionary:inArguments] : [NSMutableDictionary dictionary];
	
	NSError *error = nil;
	NSString *query = [self signedQueryFromArguments:newArgs authentication:inAuthentication error:&error];
	
	if (error != nil) {
		if ([delegate respondsToSelector:@selector(locrAPIRequest:didFailWithError:)]) {
			[delegate locrAPIRequest:self didFailWithError:error];        
		}
		
		return;
	}
	
	NSString *urlString = [NSString stringWithFormat:@"%@/%@_xml.php?%@", [context queryEndpoint], inMethodName, query];
	
    [httpRequest setContentType:nil];

	BOOL success = [httpRequest performMethod:LFHTTPRequestGETMethod onURL:[NSURL URLWithString:urlString] withData:nil];
	
	if (!success) {
		if ([delegate respondsToSelector:@selector(locrAPIRequest:didFailWithError:)]) {
			NSError *error = [NSError errorWithDomain:OLLocrAPIRequestErrorDomain
												 code:OLLocrAPIRequestConnectionError
											 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Could not connect", NSLocalizedFailureReasonErrorKey, nil]];
			
			[delegate locrAPIRequest:self didFailWithError:error];        
		}
		
		return;
	}
}

- (void)callAPIMethodWithPOST:(NSString *)inMethodName
					arguments:(NSDictionary *)inArguments
			   authentication:(BOOL)inAuthentication
{
    if ([self isRunning]) {
		if ([delegate respondsToSelector:@selector(locrAPIRequest:didFailWithError:)]) {
			NSError *error = [NSError errorWithDomain:OLLocrAPIRequestErrorDomain
												 code:OLLocrAPIRequestConnectionError
											 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Concurrent requests not supported", NSLocalizedFailureReasonErrorKey, nil]];
			
			[delegate locrAPIRequest:self didFailWithError:error];        
		}
		
		return;
    }
    
	if (inAuthentication  && (signature == nil)) {
		SEL mySelector = @selector(callAPIMethodWithPOST:arguments:authentication:);
		NSMethodSignature *mySignature = [[self class] instanceMethodSignatureForSelector:mySelector];
		NSInvocation *myInvocation = [NSInvocation invocationWithMethodSignature:mySignature];
		NSNumber *myAuthentication = [NSNumber numberWithBool:inAuthentication];
		
		[myInvocation retainArguments];
		[myInvocation setTarget:self];
		[myInvocation setSelector:mySelector];
		[myInvocation setArgument:&inMethodName atIndex:2];
		[myInvocation setArgument:&inArguments atIndex:3];
		[myInvocation setArgument:&myAuthentication atIndex:4];
		
		invocation = [myInvocation retain];
		
		[context requestSignature:self];
		
		return;
	}
	
    
    // combine the parameters 
	NSMutableDictionary *newArgs = inArguments ? [NSMutableDictionary dictionaryWithDictionary:inArguments] : [NSMutableDictionary dictionary];
	
	NSError *error = nil;
	NSString *arguments = [self signedQueryFromArguments:newArgs authentication:inAuthentication error:&error];
    
	if (error != nil) {
		if ([delegate respondsToSelector:@selector(locrAPIRequest:didFailWithError:)]) {
			[delegate locrAPIRequest:self didFailWithError:error];        
		}
		
		return;
	}
	
	NSData *postData = [arguments dataUsingEncoding:NSUTF8StringEncoding];
	NSString *urlString = [NSString stringWithFormat:@"%@/%@_xml.php", [context queryEndpoint], inMethodName];
	
	[httpRequest setContentType:LFHTTPRequestWWWFormURLEncodedContentType];

	BOOL success = [httpRequest performMethod:LFHTTPRequestPOSTMethod onURL:[NSURL URLWithString:urlString] withData:postData];
	
	if (!success) {
		if ([delegate respondsToSelector:@selector(locrAPIRequest:didFailWithError:)]) {
			NSError *error = [NSError errorWithDomain:OLLocrAPIRequestErrorDomain
												 code:OLLocrAPIRequestConnectionError
											 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Could not connect", NSLocalizedFailureReasonErrorKey, nil]];
			
			[delegate locrAPIRequest:self didFailWithError:error];        
		}
		
		return;
	}
}

- (void)uploadJPEGImageStream:(NSInputStream *)inImageStream
			suggestedFilename:(NSString *)inFilename
					arguments:(NSDictionary *)inArguments
{
    if ([self isRunning]) {
		if ([delegate respondsToSelector:@selector(locrAPIRequest:didFailWithError:)]) {
			NSError *error = [NSError errorWithDomain:OLLocrAPIRequestErrorDomain
												 code:OLLocrAPIRequestConnectionError
											 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Concurrent requests not supported", NSLocalizedFailureReasonErrorKey, nil]];
			
			[delegate locrAPIRequest:self didFailWithError:error];        
		}
		
		return;
    }
    
	if (signature == nil) {
		SEL mySelector = @selector(uploadJPEGImageStream:suggestedFilename:arguments:);
		NSMethodSignature *mySignature = [[self class] instanceMethodSignatureForSelector:mySelector];
		NSInvocation *myInvocation = [NSInvocation invocationWithMethodSignature:mySignature];
		
		[myInvocation retainArguments];
		[myInvocation setTarget:self];
		[myInvocation setSelector:mySelector];
		[myInvocation setArgument:&inImageStream atIndex:2];
		[myInvocation setArgument:&inFilename atIndex:3];
		[myInvocation setArgument:&inArguments atIndex:4];
		
		invocation = [myInvocation retain];
		
		[context requestSignature:self];
		
		return;
	}
	
    // get the api_sig
	NSError *error = nil;
    NSArray *argComponents = [self signedArgumentComponentsFromArguments:(inArguments ? inArguments : [NSDictionary dictionary]) 
															useURIEscape:NO 
														  authentication:YES
																   error:&error];
	
	if (error != nil) {
		if ([delegate respondsToSelector:@selector(locrAPIRequest:didFailWithError:)]) {
			[delegate locrAPIRequest:self didFailWithError:error];        
		}
		
		return;
	}
	
    NSString *separator = OLGenerateUUIDString();
	NSString *mimeType = @"image/jpeg";
    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", separator];
    
    // build the multipart form
    NSMutableString *multipartBegin = [NSMutableString string];
    NSMutableString *multipartEnd = [NSMutableString string];
    
    NSEnumerator *componentEnumerator = [argComponents objectEnumerator];
    NSArray *nextArgComponent;
    while (nextArgComponent = [componentEnumerator nextObject]) {        
        [multipartBegin appendFormat:@"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n", separator, [nextArgComponent objectAtIndex:0], [nextArgComponent objectAtIndex:1]];
    }
	
    // add filename, if nil, generate a UUID
    [multipartBegin appendFormat:@"--%@\r\nContent-Disposition: form-data; name=\"file\"; filename=\"%@\"\r\n", separator, [inFilename length] ? inFilename : OLGenerateUUIDString()];
    [multipartBegin appendFormat:@"Content-Type: %@\r\n\r\n", mimeType];
	
    [multipartEnd appendFormat:@"\r\n--%@--", separator];
    
    
    // now we have everything, create a temp file for this purpose; although UUID is inferior to 
    [self cleanUpTempFile];
    uploadTempFilename = [[NSTemporaryDirectory() stringByAppendingString:[NSString stringWithFormat:@"%@.%@", OLLocrUploadTempFilenamePrefix, OLGenerateUUIDString()]] retain];
    
    // create the write stream
    NSOutputStream *outputStream = [NSOutputStream outputStreamToFileAtPath:uploadTempFilename append:NO];
    [outputStream open];
    
    const char *UTF8String;
    size_t writeLength;
    UTF8String = [multipartBegin UTF8String];
    writeLength = strlen(UTF8String);
    NSAssert([outputStream write:(uint8_t *)UTF8String maxLength:writeLength] == writeLength, @"Must write multipartBegin");
	
    // open the input stream
    const size_t bufferSize = 65536;
    size_t readSize = 0;
    uint8_t *buffer = (uint8_t *)calloc(1, bufferSize);
    NSAssert(buffer, @"Must have enough memory for copy buffer");
	
    [inImageStream open];
    while ([inImageStream hasBytesAvailable]) {
        if (!(readSize = [inImageStream read:buffer maxLength:bufferSize])) {
            break;
        }
        
        NSAssert (readSize == [outputStream write:buffer maxLength:readSize], @"Must completes the writing");
    }
    
    [inImageStream close];
    free(buffer);
    
    
    UTF8String = [multipartEnd UTF8String];
    writeLength = strlen(UTF8String);
    NSAssert([outputStream write:(uint8_t *)UTF8String maxLength:writeLength] == writeLength, @"Must write multipartBegin");
    [outputStream close];
    
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4	
    NSDictionary *fileInfo = [[NSFileManager defaultManager] fileSystemAttributesAtPath:uploadTempFilename];
    NSAssert(fileInfo, @"Must have upload temp file");
#else
    NSDictionary *fileInfo = [[NSFileManager defaultManager] attributesOfItemAtPath:uploadTempFilename error:&error];
    NSAssert(fileInfo && !error, @"Must have upload temp file");
#endif

    NSNumber *fileSizeNumber = [fileInfo objectForKey:NSFileSize];
    NSUInteger fileSize = 0;
	
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4                
    fileSize = [fileSizeNumber intValue];
#else
    if ([fileSizeNumber respondsToSelector:@selector(integerValue)]) {
        fileSize = [fileSizeNumber integerValue];                    
    }
    else {
        fileSize = [fileSizeNumber intValue];                    
    }                
#endif
    
    NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:uploadTempFilename];
	
    [httpRequest setContentType:contentType];
	
	BOOL success = [httpRequest performMethod:LFHTTPRequestPOSTMethod onURL:[NSURL URLWithString:[context uploadEndpoint]] withInputStream:inputStream knownContentSize:fileSize];
	
	if (!success) {
		if ([delegate respondsToSelector:@selector(locrAPIRequest:didFailWithError:)]) {
			NSError *error = [NSError errorWithDomain:OLLocrAPIRequestErrorDomain
												 code:OLLocrAPIRequestConnectionError
											 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Could not connect", NSLocalizedFailureReasonErrorKey, nil]];
			
			[delegate locrAPIRequest:self didFailWithError:error];        
		}
		
		return;
	}
}


#pragma mark -
#pragma mark LFHTTPRequest delegate methods

- (void)httpRequestDidComplete:(LFHTTPRequest *)request
{
	NSDictionary *responseDictionary = [OLXMLMapper dictionaryMappedFromXMLData:[request receivedData]];
	NSDictionary *rootElement = (NSDictionary*)[responseDictionary olRootElement];
	NSDictionary *attributes = [rootElement olAttributes];
	NSString *status = [attributes objectForKey:@"status"];
	
	// this also fails when (responseDictionary, rsp, stat) == nil, so it's a guranteed way of checking the result
	if (![status isEqualToString:@"ok"]) {
		NSLog(@"response:\n%@", [[[NSString alloc] initWithData:[request receivedData] encoding:NSUTF8StringEncoding] autorelease]);
		
		NSDictionary *info = [rootElement objectForKey:@"info"];
		NSObject *error = [info objectForKey:@"error"];
		
		if ([error isKindOfClass:[NSArray class]]) {
			// Only handle the first error
			error = [(NSArray*)error objectAtIndex:0];
		}
		
		NSDictionary *errorAttributes = [(NSDictionary*)error olAttributes];
		NSString *code = [errorAttributes objectForKey:@"code"];
		NSString *msg = [errorAttributes objectForKey:@"message"];
		
		NSError *toDelegateError;
		if ([code length]) {
			// intValue for 10.4-compatibility
			toDelegateError = [NSError errorWithDomain:OLLocrAPIReturnedErrorDomain code:[code intValue] userInfo:[msg length] ? [NSDictionary dictionaryWithObjectsAndKeys:msg, NSLocalizedFailureReasonErrorKey, nil] : nil];				
		}
		else {
			toDelegateError = [NSError errorWithDomain:OLLocrAPIRequestErrorDomain code:OLLocrAPIRequestFaultyXMLResponseError userInfo:nil];
		}
		
		if ([delegate respondsToSelector:@selector(locrAPIRequest:didFailWithError:)]) {
			[delegate locrAPIRequest:self didFailWithError:toDelegateError];        
		}
		return;
	}
	
    [self cleanUpTempFile];
	
    if ([delegate respondsToSelector:@selector(locrAPIRequest:didCompleteWithResponse:)]) {
		[delegate locrAPIRequest:self didCompleteWithResponse:responseDictionary];
    }    
}

- (void)httpRequest:(LFHTTPRequest *)request didFailWithError:(NSString *)error
{
    NSError *toDelegateError = nil;
    if ([error isEqualToString:LFHTTPRequestConnectionError]) {
		toDelegateError = [NSError errorWithDomain:OLLocrAPIRequestErrorDomain code:OLLocrAPIRequestConnectionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Network connection error", NSLocalizedFailureReasonErrorKey, nil]];
    }
    else if ([error isEqualToString:LFHTTPRequestTimeoutError]) {
		toDelegateError = [NSError errorWithDomain:OLLocrAPIRequestErrorDomain code:OLLocrAPIRequestTimeoutError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Request timeout", NSLocalizedFailureReasonErrorKey, nil]];
    }
    else {
		toDelegateError = [NSError errorWithDomain:OLLocrAPIRequestErrorDomain code:OLLocrAPIRequestUnknownError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unknown error", NSLocalizedFailureReasonErrorKey, nil]];
    }
    
    [self cleanUpTempFile];
    if ([delegate respondsToSelector:@selector(locrAPIRequest:didFailWithError:)]) {
        [delegate locrAPIRequest:self didFailWithError:toDelegateError];        
    }
}

- (void)httpRequest:(LFHTTPRequest *)request sentBytes:(NSUInteger)bytesSent total:(NSUInteger)total
{
    if (uploadTempFilename && [delegate respondsToSelector:@selector(locrAPIRequest:imageUploadSentBytes:totalBytes:)]) {
        [delegate locrAPIRequest:self imageUploadSentBytes:bytesSent totalBytes:total];
    }
}

@end


@implementation OLLocrAPIRequest (PrivateMethods)

- (void)cleanUpTempFile

{
    if (uploadTempFilename) {
        NSFileManager *fileManager = [NSFileManager defaultManager];
        if ([fileManager fileExistsAtPath:uploadTempFilename]) {
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4	
            NSAssert([fileManager removeFileAtPath:uploadTempFilename handler:NULL], @"Should be able to remove temp file");
#else
            NSError *error = nil;
            NSAssert([fileManager removeItemAtPath:uploadTempFilename error:&error], @"Should be able to remove temp file");
#endif
        }
        
        [uploadTempFilename release];
        uploadTempFilename = nil;
    }
}

- (NSArray *)signedArgumentComponentsFromArguments:(NSDictionary *)inArguments
									  useURIEscape:(BOOL)inUseEscape
									authentication:(BOOL)inAuthentication
											 error:(NSError**)error
{
	NSMutableDictionary *newArgs = [NSMutableDictionary dictionaryWithDictionary:inArguments];
	
	if (inAuthentication) {
		NSString *userName = [context userName];
		NSString *password = [context password];
		
		if ((userName != nil) && (password != nil)) {
			if (signature != nil) {
				NSString *token = OLMD5HexStringFromNSString([NSString stringWithFormat:@"%@%@", OLMD5HexStringFromNSString(password), signature]);
				
				[newArgs setObject:signature forKey:@"api_signature"];
				[newArgs setObject:userName forKey:@"auth_user_name"];
				[newArgs setObject:token forKey:@"auth_token"];
			}
			else if (error != nil) {
				*error = [NSError errorWithDomain:OLLocrAPIRequestErrorDomain
											 code:OLLocrAPIRequestAuthenticationError
										 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Missing API signature", NSLocalizedFailureReasonErrorKey, nil]];
			}
		}
		else if (error != nil) {
			*error = [NSError errorWithDomain:OLLocrAPIRequestErrorDomain
										 code:OLLocrAPIRequestAuthenticationError
									 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Missing user name or password", NSLocalizedFailureReasonErrorKey, nil]];
		}
	}
	
	NSMutableArray *argArray = [NSMutableArray array];
	NSArray *sortedArgs = [[newArgs allKeys] sortedArrayUsingSelector:@selector(compare:)];
	NSEnumerator *argEnumerator = [sortedArgs objectEnumerator];
	
	NSString *nextKey;
	while (nextKey = [argEnumerator nextObject]) {
		NSString *value = [newArgs objectForKey:nextKey];
		
		[argArray addObject:[NSArray arrayWithObjects:nextKey, (inUseEscape ? OLEscapedURLStringFromNSString(value) : value), nil]];
	}
	
	return argArray;
}

- (NSString *)signedQueryFromArguments:(NSDictionary *)inArguments authentication:(BOOL)inAuthentication error:(NSError**)error
{
    NSArray *argComponents = [self signedArgumentComponentsFromArguments:inArguments useURIEscape:YES authentication:inAuthentication error:error];
	NSMutableArray *args = [NSMutableArray array];
    NSEnumerator *componentEnumerator = [argComponents objectEnumerator];
    NSArray *nextArg;
    while (nextArg = [componentEnumerator nextObject]) {
        [args addObject:[nextArg componentsJoinedByString:@"="]];
    }
    
    return [args componentsJoinedByString:@"&"];
}

- (void)context:(OLLocrAPIContext*)inContext providesSignature:(NSString*)inSignature
{
	[signature release], signature = [inSignature copy];
	
	if (invocation != nil) {
		NSInvocation *myInvocation = [invocation retain];
		
		[invocation release], invocation = nil;

		[myInvocation invoke];
		[myInvocation release];
	}
	
	[self performSelector:@selector(dropSignature) withObject:nil afterDelay:60];
}

- (void)context:(OLLocrAPIContext*)inContext failedToProvideSignatureWithError:(NSError*)inError
{
	[signature release], signature = nil;
	[invocation release], invocation = nil;
	
	if ([delegate respondsToSelector:@selector(locrAPIRequest:didFailWithError:)]) {
		NSError *error = [NSError errorWithDomain:OLLocrAPIRequestErrorDomain
											 code:OLLocrAPIRequestConnectionError
										 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to acquire API signature", NSLocalizedFailureReasonErrorKey, nil]];
		
		[delegate locrAPIRequest:self didFailWithError:error];        
	}
}

- (void)dropSignature
{
	[signature release], signature = nil;
}

@end